package belf.migrate.core.jdbc;

import belf.migrate.core.exception.ParseMappingFileError;
import belf.migrate.core.util.ConfigUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.sql.PreparedStatement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class DBFieldConvert {
    private static final Logger log = LoggerFactory.getLogger(DBFieldConvert.class);

    private final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

    private static final DBFieldConvert instance = new DBFieldConvert();

    //主键为：databaseName.tableName.columnName
    private final Map<String, DBFieldConvertRule> mappings = new HashMap<>();

    private DBFieldConvert() {
        File path = new File(ConfigUtil.getDBFieldConvertDir());
        Arrays.stream(
                        path.list())
                .filter(name-> name.endsWith(".xml"))
                .forEach(s -> {
                    try {
                        parseRuleFile(path.getAbsolutePath() + File.separator + s);
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("Parse DB Field Convert error: {}", s);
                    }

                });
    }

    private void parseRuleFile(String filename) {
        try {
            DocumentBuilder domBuilder = documentBuilderFactory.newDocumentBuilder();
            Document doc = domBuilder.parse(new InputSource(new InputStreamReader(new FileInputStream(filename))));
            Element tables = doc.getDocumentElement();
            String databaseName = tables.getAttribute("databaseName");
            NodeList nodes = tables.getElementsByTagName("table");
            if (nodes == null || nodes.getLength() == 0) return;

            for (int i = 0; i < nodes.getLength(); i++) {
                Element table = (Element)nodes.item(i);
                String tableName = table.getAttribute("tableName");
                NodeList fieldNodes = table.getElementsByTagName("field");
                if (fieldNodes == null || fieldNodes.getLength() == 0) return;

                for (int j = 0; j < fieldNodes.getLength(); j++) {
                    Element field = (Element)fieldNodes.item(j);
                    String columnName = field.getAttribute("columnName");
                    String mapKey = getMapKey(databaseName, tableName, columnName);
                    Element input = (Element) field.getElementsByTagName("input").item(0);
                    Element output = (Element) field.getElementsByTagName("output").item(0);
                    Element sqlFormat = (Element) field.getElementsByTagName("sqlFormat").item(0);
                    DBFieldConvertRule convertRule = new DBFieldConvertRule(databaseName,
                            tableName,
                            columnName,
                            input.getAttribute("type"),
                            output.getAttribute("type"),
                            input.getAttribute("jsonKey"),
                            output.getAttribute("method"),
                            sqlFormat.getTextContent().trim());
                    log.info("Parsed: {}", convertRule);
                    mappings.put(mapKey, convertRule);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static DBFieldConvert getInstance() {
        return instance;
    }

    public DBFieldConvertRule getConvertRule(String databaseName, String tableName, String columnName) {
        String mapKey = getMapKey(databaseName, tableName, columnName);
        return mappings.get(mapKey);
    }

    private String getMapKey(String databaseName, String tableName, String columnName) {
        return databaseName.toUpperCase()+ "." + tableName.toUpperCase() + "." + columnName.toUpperCase();
    }

    @Data
    @AllArgsConstructor
    public static class DBFieldConvertRule {
        private String databaseName;

        private String tableName;

        private String columnName;

        private String inputType;

        private String outputType;

        private String jsonKey;

        private String method;

        private String sqlFormat;

    }
}
